home *** CD-ROM | disk | FTP | other *** search
- //
- // This is a patch for Inventor 2.0's picking routines, which are
- // extremely slow when doing world-space picking.
- //
- // To apply this patch, compile this file into a .o and then link
- // the .o before -lInventor.
- // The linker may give a warning about
- // multiply defined symbols. This is normal and expected.
- //
-
- #include <Inventor/SbLinear.h>
- #include <Inventor/SbBox.h>
-
- ////////////////////////////////////////////////////////////////////////
- //
- // Description:
- // Intersects view volume with box. Returns TRUE if intersection.
- //
- // Use: internal
-
- SbBool
- SbViewVolume::intersect(const SbBox3f &box) const
- //
- ////////////////////////////////////////////////////////////////////////
- {
- // Empty bboxes can cause problems:
- if (box.isEmpty()) return FALSE;
-
- //
- // The bounding box is the set of all points between its bounds;
- // that is, all points (x,y,z) such that:
- // x_min <= x <= x_max
- // y_min <= y <= y_max
- // z_min <= z <= z_max
- // (Inventor bounding boxes are axis-aligned)
- //
- // We will consider there to be no intersection if all of those
- // points are on the 'outside' side of any of the view-volume
- // planes (this may pass some things that could be culled, but
- // will never cull things that are visible). So we want to see if
- // any point is on the inside, in which case the intersection
- // returns TRUE.
- //
- // The equation of the view-volume planes is Ax+By+Cz=D, where
- // (A,B,C) is the plane normal and D is the distance from the
- // origin. The condition for a point (x,y,z) being on the
- // 'outside' side is Ax+By+Cz-D < 0. We need to test the largest
- // value of Ax+By+Cz-D to see if it is negative. If it is, then we
- // know all points are outside.
- //
- // We can minimize the number of point/plane checks we do by
- // noticing that Ax+By+Cx-D will be greatest when each of its
- // terms is greatest; for example, if A is positive, Ax will be
- // greatest when x is x_max. If A is negative, Ax will be greatest
- // when x is x_min.
- // So, we can reduce the test to a check of one point against each
- // of the 6 plane equations. The outsideTest() method does this.
- //
-
- SbVec3f min, max;
- box.getBounds(min, max);
-
- // OPPORTUNITY FOR OPTIMIZATION HERE: We could precompute planes
- // and save some work.
-
- if (type == PERSPECTIVE) {
- // Left plane is formed from eye, llf, ulf
- SbPlane leftPlane(projPoint, llf, ulf);
- if (outsideTest(leftPlane, min, max))
- return FALSE;
-
- // Figure out urf point:
- SbVec3f urf = lrf + (ulf - llf);
- // Right is eye, urf, lrf
- SbPlane rightPlane(projPoint, urf, lrf);
- if (outsideTest(rightPlane, min, max))
- return FALSE;
-
- // Near is lrf, llf, ulf:
- SbPlane nearPlane(lrf, llf, ulf);
- if (outsideTest(nearPlane, min, max))
- return FALSE;
-
- // Far is near points in opposite order, translated to far plane:
- SbVec3f farOffset = nearToFar * projDir;
- SbPlane farPlane(ulf+farOffset, llf+farOffset, lrf+farOffset);
- if (outsideTest(farPlane, min, max))
- return FALSE;
-
- // Bottom is eye, lrf, llf
- SbPlane bottomPlane(projPoint, lrf, llf);
- if (outsideTest(bottomPlane, min, max))
- return FALSE;
-
- // Finally, top is eye, ulf, urf
- SbPlane topPlane(projPoint, ulf, urf);
- if (outsideTest(topPlane, min, max))
- return FALSE;
- }
-
- else { // type == ORTHOGRAPHIC
- // Left plane is formed from llf, lff+projDir, ulf
- SbPlane leftPlane(llf, llf+projDir, ulf);
- if (outsideTest(leftPlane, min, max))
- return FALSE;
-
- // Figure out urf point:
- SbVec3f urf = lrf + (ulf - llf);
- // Right is urf+projDir, lrf, urf
- SbPlane rightPlane(urf+projDir, lrf, urf);
- if (outsideTest(rightPlane, min, max))
- return FALSE;
-
- // Near is lrf, llf, ulf:
- SbPlane nearPlane(lrf, llf, ulf);
- if (outsideTest(nearPlane, min, max))
- return FALSE;
-
- // Far is near points in opposite order, translated to far plane:
- SbVec3f farOffset = nearToFar * projDir;
- SbPlane farPlane(ulf+farOffset, llf+farOffset, lrf+farOffset);
- if (outsideTest(farPlane, min, max))
- return FALSE;
-
- // Bottom is lrf, lrf+projDir, lrf, llf
- SbPlane bottomPlane(lrf, lrf+projDir, llf);
- if (outsideTest(bottomPlane, min, max))
- return FALSE;
-
- // Finally, top is urf, ulf, ulf+projDir
- SbPlane topPlane(urf, ulf, ulf+projDir);
- if (outsideTest(topPlane, min, max))
- return FALSE;
- }
-
- return TRUE;
- }
-
- #include <Inventor/actions/SoRayPickAction.h>
-
- ////////////////////////////////////////////////////////////////////////
- //
- // Description:
- // Intersects object-space bounding box with current ray. Returns
- // success or failure.
- //
- // Use: extender
-
- SbBool
- SoRayPickAction::intersect(const SbBox3f &box)
- //
- ////////////////////////////////////////////////////////////////////////
- {
- // If the ray was set as a world-space line, we don't really have
- // a valid object-space view volume to test against the box. (The
- // view volume is a degenerate case - a line.) So just use the
- // object-space line to do the intersection.
- if (lineWasSet) {
- SbVec3f enter, exit;
- return objLine.intersect(box, enter, exit);
- }
-
- else
- return objVol.intersect(box);
- }
-
- ////////////////////////////////////////////////////////////////////////
- //
- // Description:
- // Intersects the line with the triangle formed bu v0, v1, v2.
- // Returns TRUE if there is an intersection. If there is an
- // intersection, barycentric will contain the barycentric
- // coordinates of the intersection (for v0, v1, v2, respectively)
- // and front will be set to TRUE if the ray intersected the front
- // of the triangle (as defined by the right hand rule).
- //
- // Use: internal
-
- SbBool
- SbLine::intersect(const SbVec3f &v0, const SbVec3f &v1, const SbVec3f &v2,
- SbVec3f &intersection,
- SbVec3f &barycentric, SbBool &front) const
- //
- ////////////////////////////////////////////////////////////////////////
- {
- //////////////////////////////////////////////////////////////////
- //
- // The method used here is described by Didier Badouel in Graphics
- // Gems (I), pages 390 - 393.
- //
- //////////////////////////////////////////////////////////////////
-
- #define EPSILON 1e-10
-
- //
- // (1) Compute the plane containing the triangle
- //
- SbVec3f v01 = v1 - v0;
- SbVec3f v12 = v2 - v1;
- SbVec3f norm = v12.cross(v01); // Un-normalized normal
- // Normalize normal to unit length, and make sure the length is
- // not 0 (indicating a zero-area triangle)
- if (norm.normalize() < EPSILON)
- return FALSE;
-
- //
- // (2) Compute the distance t to the plane along the line
- //
- float d = getDirection().dot(norm);
- if (d < EPSILON && d > -EPSILON)
- return FALSE; // Line is parallel to plane
- float t = norm.dot(v0 - getPosition()) / d;
- if (t <= 0.0) // Ignore intersections behind eye
- return FALSE;
-
- //
- // (3) Find the largest component of the plane normal. The other
- // two dimensions are the axes of the aligned plane we will
- // use to project the triangle.
- //
- float xAbs = norm[0] < 0.0 ? -norm[0] : norm[0];
- float yAbs = norm[1] < 0.0 ? -norm[1] : norm[1];
- float zAbs = norm[2] < 0.0 ? -norm[2] : norm[2];
- int axis0, axis1;
-
- if (xAbs > yAbs && xAbs > zAbs) {
- axis0 = 1;
- axis1 = 2;
- }
- else if (yAbs > zAbs) {
- axis0 = 2;
- axis1 = 0;
- }
- else {
- axis0 = 0;
- axis1 = 1;
- }
-
- //
- // (4) Determine if the projected intersection (of the line and
- // the triangle's plane) lies within the projected triangle.
- // Since we deal with only 2 components, we can avoid the
- // third computation.
- //
- float intersection0 = getPosition()[axis0] + t * getDirection()[axis0];
- float intersection1 = getPosition()[axis1] + t * getDirection()[axis1];
-
- SbVec2f diff0, diff1, diff2;
- SbBool isInter = FALSE;
- float alpha, beta;
-
- diff0[0] = intersection0 - v0[axis0];
- diff0[1] = intersection1 - v0[axis1];
- diff1[0] = v1[axis0] - v0[axis0];
- diff1[1] = v1[axis1] - v0[axis1];
- diff2[0] = v2[axis0] - v0[axis0];
- diff2[1] = v2[axis1] - v0[axis1];
-
- isInter = FALSE;
- if (diff1[0] < EPSILON && diff1[0] > -EPSILON) {
- beta = diff0[0] / diff2[0];
- if (beta >= 0.0 && beta <= 1.0) {
- alpha = (diff0[1] - beta * diff2[1]) / diff1[1];
- isInter = (alpha >= 0.0 && alpha + beta <= 1.0);
- }
- }
- else {
- beta = ((diff0[1] * diff1[0] - diff0[0] * diff1[1]) /
- (diff2[1] * diff1[0] - diff2[0] * diff1[1]));
- if (beta >= 0.0 && beta <= 1.0) {
- alpha = (diff0[0] - beta * diff2[0]) / diff1[0];
- isInter = (alpha >= 0.0 && alpha + beta <= 1.0);
- }
- }
-
- //
- // (5) If there is an intersection, set up the barycentric
- // coordinates and figure out if the front was hit.
- //
- if (isInter) {
- barycentric.setValue(1.0 - (alpha + beta), alpha, beta);
- front = (getDirection().dot(norm) < 0.0);
- intersection = getPosition() + t * getDirection();
- }
-
- return isInter;
-
- #undef EPSILON
- }
-
- #include <Inventor/actions/SoPickAction.h>
-
- ////////////////////////////////////////////////////////////////////////
- //
- // Description:
- // Constructor.
- //
- // Use: protected
-
- SoPickAction::SoPickAction(const SbViewportRegion &viewportRegion)
- //
- ////////////////////////////////////////////////////////////////////////
- {
- SO_ACTION_CONSTRUCTOR(SoPickAction);
- vpRegion = viewportRegion;
-
- enableCulling(TRUE);
- }
-
- #include <Inventor/SoPickedPoint.h>
- #include <Inventor/elements/SoModelMatrixElement.h>
-
- ////////////////////////////////////////////////////////////////////////
- //
- // Description:
- // Sets the object-space normal.
- //
- // Use: extender
-
- void
- SoPickedPoint::setObjectNormal(const SbVec3f &normal)
- //
- ////////////////////////////////////////////////////////////////////////
- {
- // Transform the object space normal by the current modeling
- // matrix o get the world space normal. Use the inverse transpose
- // of the odel matrix so that normals are not scaled incorrectly.
- SbMatrix normalMatrix =
- SoModelMatrixElement::get(state).inverse().transpose();
-
- normalMatrix.multDirMatrix(normal, worldNormal);
- worldNormal.normalize();
- }
-